package cn.cloudtogo.server.module.datasource;

import cn.cloudtogo.common.Assert;
import cn.cloudtogo.common.DatasourcePutService;
import cn.cloudtogo.common.model.ExternalDatasourceDao;
import cn.cloudtogo.common.model.ExternalDatasourceModel;
import cn.cloudtogo.common.types.ConnectString;
import cn.cloudtogo.common.types.RequestHeaderObject;
import cn.cloudtogo.common.util.JdbcConnectionHelper;
import cn.cloudtogo.common.util.StringHelper;
import cn.cloudtogo.server.mysql.SpringJdbcTemplateContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author yaolianhua789@gmail.com
 **/
@Service
@Slf4j
public class ExternalDatasourcePutService implements DatasourcePutService {


    private final ExternalDatasourceDao externalDatasourceDao;
    private final SpringJdbcTemplateContext context;

    public ExternalDatasourcePutService(ExternalDatasourceDao externalDatasourceDao,
                                        SpringJdbcTemplateContext context) {
        this.externalDatasourceDao = externalDatasourceDao;
        this.context = context;
    }

    @Override
    public void updateConnectionInfoByOpenid(String openid, String url) {

        List<ExternalDatasourceModel> models = externalDatasourceDao.list(openid);
        if (CollectionUtils.isEmpty(models)) {
            log.warn("can not get any data [{}]. nothing will be update", openid);
            return;
        }

        ConnectString connectString = ConnectString.getConnectString(url);

        //check connection
        boolean validConnection = JdbcConnectionHelper.isValidConnection(connectString);
        if (!validConnection) {
            log.error("invalid datasource connection [{}]", url);
            throw new CannotGetJdbcConnectionException("中间件连接失败");
        }


        List<ExternalDatasourceModel> updateModels = models
                .stream()
                .filter(e -> this.hasChanged(e,
                        connectString.getCredential().getUser(),
                        connectString.getCredential().getPassword(),
                        connectString.getEndpoint().getHost(),
                        connectString.getEndpoint().getPort(),
                        connectString.getSchema())
                ).collect(Collectors.toList());
        updateModels
                .stream()
                .peek(e -> {
                    e.setUsername(connectString.getCredential().getUser());
                    e.setPassword(connectString.getCredential().getPassword());
                    e.setHost(connectString.getEndpoint().getHost());
                    e.setPort(connectString.getEndpoint().getPort());
                    e.setSchema(connectString.getSchema());
                    e.setUrl(String.format("jdbc:mysql//%s:%s/%s", connectString.getEndpoint().getHost(), connectString.getEndpoint().getPort(), connectString.getSchema()));

                }).forEach(externalDatasourceDao::updateByUuid);
        updateModels.stream().map(RequestHeaderObject::ofModel).forEach(context::remove);

    }

    @Override
    public void updateNameByUuid(String id, String name) {
        ExternalDatasourceModel model = externalDatasourceDao.query(id);
        Assert.notNull(model, () -> String.format("record does not found from '%s'", id));
        if (Objects.equals(model.getName(), name)) {
            log.info("no data will be update '{}'", name);
            return;
        }
        model.setName(name);
        String project = obtainProject(null, model.getOpenid(), name, model.getUrl(), model.getSchema());
        model.setProject(project);

        externalDatasourceDao.updateByUuid(model);
    }

    private boolean hasChanged(ExternalDatasourceModel model, String user, String password, String host, Integer port, String schema) {
        return !Objects.equals(model.getUsername(), user) ||
                !Objects.equals(model.getPassword(), password) ||
                !Objects.equals(model.getHost(), host) ||
                !Objects.equals(model.getPort(), port) ||
                !Objects.equals(model.getSchema(), schema);
    }

    private synchronized String obtainProject(String project, String openid, String name, String url, String schema) {

        if (StringUtils.hasText(project)) {
            return project;
        }

        long currentTimeMillis = System.currentTimeMillis();
        project = StringHelper.encode("|", new String[]{url, schema, openid, name, String.valueOf(currentTimeMillis)});

        return project;
    }
}
